package util;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import junit.framework.TestCase;
/**
 * Chargeur de classe test
 */
public class TestLoader extends ClassLoader {
	
	private List<Class<TestCase>> classes;
	
	public static String pluginsDirectory = "plugins/repository";
	// pour eclipse, ignore la racine bin
	public static String binDirectory = "bin";
	// pour ant, ignore la racine build
	public static String buildDirectory = "build/classes";
	public static String classExt = ".class";
	
	/**
	 * Constructeur par defaut
	 * @throws ClassNotFoundException
	 */
	public TestLoader() throws ClassNotFoundException {
		super();
		classes = new ArrayList<Class<TestCase>>();
		load();
	}
	
	/**
	 * Cherche dans le repertoire courant si il y a des tests
	 * @param path : le chemin courant
	 * @throws ClassNotFoundException
	 */
	public void getClassFromFolder(String path) throws ClassNotFoundException {
		final File folder = new File(path);
	    for (final File fileEntry : folder.listFiles()) {
	        if (fileEntry.isDirectory()) {
	        	if (pluginsDirectory.contains(fileEntry.getName()) ||
	        			binDirectory.contains(fileEntry.getName()) ||
	        			buildDirectory.contains(fileEntry.getName())) {
	        		getClassFromSubFolder(fileEntry, "");
	        	} else {
	        		getClassFromSubFolder(fileEntry, fileEntry.getName() + ".");
	        	}
	        } else if (fileEntry.getName().endsWith("Test.class")) {
	        	Class<TestCase> test = loadOneTest(removeExt(fileEntry.getName(), classExt));
	        	if (test != null) classes.add(test);
	        }
	    }
	}
	
	/**
	 * Regarde dans un sous-dossier
	 * @param folder : le dossier courant
	 * @param basename : le nom du package construit par rapport au dossier 
	 * parcouru precedemment
	 * @throws ClassNotFoundException
	 */
	
	private void getClassFromSubFolder(File folder, String basename) throws ClassNotFoundException {
		for (final File fileEntry : folder.listFiles()) {
	        if (fileEntry.isDirectory()) {
	        	if (pluginsDirectory.contains(fileEntry.getName()) ||
	        			buildDirectory.contains(fileEntry.getName())) {
	        		getClassFromSubFolder(fileEntry, "");
	        	} else {
	        		getClassFromSubFolder(fileEntry, basename + fileEntry.getName() + ".");
	        	}
	        } else if (fileEntry.getName().endsWith("Test.class")) {
	        	Class<TestCase> test = loadOneTest(basename + removeExt(fileEntry.getName(), classExt));
	        	if (test != null) classes.add(test);
	        }
	    }
	}
	
	/**
	 * Charge une classe de type TestCase
	 * @param name : le nom de la classe, package inclus
	 * @return null si la classe n'extends pas TestCase
	 * @throws ClassNotFoundException
	 */
	
	public Class<TestCase> loadOneTest(String name) throws ClassNotFoundException {
		Class<?> loadedClass =null;
		try {
		loadedClass = loadClass(name);
		} catch (ClassNotFoundException e) {
			System.out.println(name);
			throw new ClassNotFoundException();
		}
		if (isSuitableForTest(loadedClass)) {
			return (Class<TestCase>) loadedClass;
		}
		return null;	
	}
	
	/**
	 * Regarde si un fichier class 
	 * @param cl : la classe courante
	 * @return true si la classe est une classe de type TestCase, false sinon
	 * @throws ClassNotFoundException
	 */
	
	public boolean isSuitableForTest(Class<?> cl) throws ClassNotFoundException {
		return Class.forName("junit.framework.TestCase").isAssignableFrom(cl);	
	}
	
	/**
	 * Cherche les classes de test a partir du dossier courant
	 * @throws ClassNotFoundException
	 */
	public void load() throws ClassNotFoundException {
		getClassFromFolder(System.getProperty("user.dir"));
	}
	
	/**
	 * Recherche a nouveau les classes de test a partir du dossier courant
	 * @throws ClassNotFoundException
	 */
	public void reload() throws ClassNotFoundException {
		eraseTestClasses();
		load();
	}
	
	/**
	 * Retourne les classes 
	 * @return
	 * @throws ClassNotFoundException
	 */
	public List<Class<TestCase>> getTestClasses () throws ClassNotFoundException {
		return classes;
	}
	
	/**
	 * Efface les tests trouves de la liste
	 */
	public void eraseTestClasses() {
		classes.clear();
	}
	
	/**
	 * Retire l'extension d'une classe 
	 * @param className : le nom de la classe, package inclus
	 * @param ext : l'extension a retirer
	 * @return le nom complet de la classe
	 */
	public String removeExt(String className, String ext) {
		return className.substring(0, className.length() - ext.length());
	}
}
